// 版权所有2009 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 垃圾收集器：终结器和块分析。

package runtime

import (
	"internal/abi"
	"internal/goarch"
	"runtime/internal/atomic"
	"unsafe"
)

// finblock是要执行的终结器数组。finblocks是
// 排列在终结器队列的链接列表中。
// 
// finblock是从非GC的内存中分配的，因此任何堆指针
// 都必须经过特殊处理。GC当前假定终结器
// 队列在标记期间不会增长（但它可能会收缩）。
// 
// go:notinheap 
type finblock struct {
	alllink *finblock
	next    *finblock
	cnt     uint32
	_       int32
	fin     [(_FinBlockSize - 2*goarch.PtrSize - 2*4) / unsafe.Sizeof(finalizer{})]finalizer
}

var finlock mutex  // 保护以下变量
var fing *g        // 运行终结器的goroutine 
var finq *finblock // 将要执行的终结器列表
var finc *finblock // 空闲块缓存
var finptrmask [_FinBlockSize / goarch.PtrSize / 8]byte
var fingwait bool
var fingwake bool
var allfin *finblock // 所有块列表

// 注意：队列终结器已知的布局。
type finalizer struct {
	fn   *funcval       // 调用函数（可能是堆指针）
	arg  unsafe.Pointer // ptr to object（可能是堆指针）
	nret uintptr        // fn返回值字节
	fint *_type         // fn 
	ot   *ptrtype       // ptr to object类型（可能是堆指针）
}

var finalizer1 = [...]byte{
	// 每个终结器是5个字，ptr ptr INT ptr ptr（这里INT=uintpttr）
	// 每个字节描述8个字。
	// 在模式重复之前需要8个以5字节描述的终结器：
	// ptr ptr INT ptr ptr 
	// ptr ptr INT ptr ptr ptr 
	// ptr ptr INT ptr ptr ptr 
	// ptr ptr ptr INT ptr ptr ptr ptr ptr 
	// ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr 
	// ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr CDEFG
	// /又名
	// 
	// ptr ptr ptr ptr ptr INT ptr ptr ptr 
	// ptr INT ptr ptr ptr ptr INT ptr 
	// ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr 
	// ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr 
	1<<0 | 1<<1 | 0<<2 | 1<<3 | 1<<4 | 1<<5 | 1<<6 | 0<<7,
	1<<0 | 1<<1 | 1<<2 | 1<<3 | 0<<4 | 1<<5 | 1<<6 | 1<<7,
	1<<0 | 0<<1 | 1<<2 | 1<<3 | 1<<4 | 1<<5 | 0<<6 | 1<<7,
	1<<0 | 1<<1 | 1<<2 | 0<<3 | 1<<4 | 1<<5 | 1<<6 | 1<<7,
	0<<0 | 1<<1 | 1<<2 | 1<<3 | 1<<4 | 0<<5 | 1<<6 | 1<<7,
}

func queuefinalizer(p unsafe.Pointer, fn *funcval, nret uintptr, fint *_type, ot *ptrtype) {
	if gcphase != _GCoff {
		// 目前，我们假设终结器队列在标记期间不会增长，因此在标记终止期间，我们不必重新扫描它。如果我们需要取消
		// 这个假设，我们可以通过向queuefinalizer添加
		// 必要的障碍（它可能会自动设置）。
		throw("queuefinalizer during GC")
	}

	lock(&finlock)
	if finq == nil || finq.cnt == uint32(len(finq.fin)) {
		if finc == nil {
			finc = (*finblock)(persistentalloc(_FinBlockSize, 0, &memstats.gcMiscSys))
			finc.alllink = allfin
			allfin = finc
			if finptrmask[0] == 0 {
				// 为块中的终结器数组构建指针掩码。
				// 检查上面finalizer1数组中的假设。
				if (unsafe.Sizeof(finalizer{}) != 5*goarch.PtrSize ||
					unsafe.Offsetof(finalizer{}.fn) != 0 ||
					unsafe.Offsetof(finalizer{}.arg) != goarch.PtrSize ||
					unsafe.Offsetof(finalizer{}.nret) != 2*goarch.PtrSize ||
					unsafe.Offsetof(finalizer{}.fint) != 3*goarch.PtrSize ||
					unsafe.Offsetof(finalizer{}.ot) != 4*goarch.PtrSize) {
					throw("finalizer out of sync")
				}
				for i := range finptrmask {
					finptrmask[i] = finalizer1[i%len(finalizer1)]
				}
			}
		}
		block := finc
		finc = block.next
		block.next = finq
		finq = block
	}
	f := &finq.fin[finq.cnt]
	atomic.Xadd(&finq.cnt, +1) // 与markroots同步
	f.fn = fn
	f.nret = nret
	f.fint = fint
	f.ot = ot
	f.arg = p
	fingwake = true
	unlock(&finlock)
}

// go:nowritebarrier 
func iterate_finq(callback func(*funcval, unsafe.Pointer, uintptr, *_type, *ptrtype)) {
	for fb := allfin; fb != nil; fb = fb.alllink {
		for i := uint32(0); i < fb.cnt; i++ {
			f := &fb.fin[i]
			callback(f.fn, f.arg, f.nret, f.fint, f.ot)
		}
	}
}

func wakefing() *g {
	var res *g
	lock(&finlock)
	if fingwait && fingwake {
		fingwait = false
		fingwake = false
		res = fing
	}
	unlock(&finlock)
	return res
}

var (
	fingCreate  uint32
	fingRunning bool
)

func createfing() {
	// 只启动一次终结器goroutine 
	if fingCreate == 0 && atomic.Cas(&fingCreate, 0, 1) {
		go runfinq()
	}
}

// 这是运行所有终结器的goroutine 
func runfinq() {
	var (
		frame    unsafe.Pointer
		framecap uintptr
		argRegs  int
	)

	for {
		lock(&finlock)
		fb := finq
		finq = nil
		if fb == nil {
			gp := getg()
			fing = gp
			fingwait = true
			goparkunlock(&finlock, waitReasonFinalizerWait, traceEvGoBlock, 1)
			continue
		}
		argRegs = intArgRegs
		unlock(&finlock)
		if raceenabled {
			racefingo()
		}
		for fb != nil {
			for i := fb.cnt; i > 0; i-- {
				f := &fb.fin[i-1]

				var regs abi.RegArgs
				// 参数可以在寄存器或堆栈中传递。即使对于
				// 注册箱，我们仍然需要溢出槽。
				// 待办事项：如果我们移除溢出槽，请重新访问。
				// 
				// 不幸的是，我们可以有一个任意的
				// 返回量，尝试和
				// 找出其中有多少可以在寄存器中传递，
				// 只是保守地假设它们都不能传递。
				framesz := unsafe.Sizeof((any)(nil)) + f.nret
				if framecap < framesz {
					// 框架不包含GC感兴趣的指针，
					// 所有尚未完成的对象都存储在finq中。
					// 如果我们不将其标记为FlagNoScan，
					// 则不会收集最后一个最终确定的对象。
					frame = mallocgc(framesz, nil, true)
					framecap = framesz
				}

				if f.fint == nil {
					throw("missing type in runfinq")
				}
				r := frame
				if argRegs > 0 {
					r = unsafe.Pointer(&regs.Ints)
				} else {
					// 帧实际上是未初始化的
					// 内存。这意味着我们必须在写入前清除
					// 以避免混淆写入障碍。
					*(*[2]uintptr)(frame) = [2]uintptr{}
				}
				switch f.fint.kind & kindMask {
				case kindPtr:
					// 直接使用指针
					*(*unsafe.Pointer)(r) = f.arg
				case kindInterface:
					ityp := (*interfacetype)(unsafe.Pointer(f.fint))
					// 设置空接口
					(*eface)(r)._type = &f.ot.typ
					(*eface)(r).data = f.arg
					if len(ityp.mhdr) != 0 {
						// 转换为方法接口
						// 此转换保证成功-在从markroot隐藏之前，我们检查了SetFinalizer 
						(*iface)(r).tab = assertE2I(ityp, (*eface)(r)._type)
					}
				default:
					throw("bad kind in runfinq")
				}
				fingRunning = true
				reflectcall(nil, unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz), uint32(framesz), &regs)
				fingRunning = false

				// 删除终结器队列堆引用
				// 。
				// 这也确保了如果我们重新使用终结器，这些将被清除。
				f.fn = nil
				f.arg = nil
				f.ot = nil
				atomic.Store(&fb.cnt, i-1)
			}
			next := fb.next
			lock(&finlock)
			fb.next = finc
			finc = fb
			unlock(&finlock)
			fb = next
		}
	}
}

// SetFinalizer将与obj关联的终结器设置为提供的
// 终结器函数。当垃圾收集器发现一个不可访问的块
// 和一个关联的终结器时，它会清除关联，并在一个单独的goroutine中运行
// 终结器（obj）。这使obj再次可访问，
// 但现在没有相关的终结器。假设SetFinalizer 
// 不再被调用，那么下次垃圾收集器看到
// 如果obj无法访问，它将释放obj。
// 
// SetFinalizer（obj，nil）清除与obj关联的任何终结器。
// 
// 参数obj必须是指向通过调用
// new、采用复合文字的地址或采用局部变量的
// 地址分配的对象的指针。
// 参数终结器必须是一个函数，该函数接受一个参数
// 可以将obj的类型分配给该参数，并且可以任意忽略返回值
// 值。如果其中一项不正确，SetFinalizer可能会中止
// 程序。
// 
// 终结器按依赖顺序运行：如果A指向B，两者都有
// 终结器，否则无法访问它们，只有终结器
// 用于A运行；一旦A被释放，B的终结器就可以运行。
// 如果循环结构包含带有终结器的块，则该
// 循环不保证被垃圾收集，终结器
// 也不保证运行，因为
// 不遵守依赖项的顺序。
// 
// 终结器计划在
// 程序无法再到达obj指向的对象后的任意时间运行。
// 不能保证终结器会在程序退出之前运行，所以它们通常只用于释放在长时间运行的程序中与对象关联的非内存资源。
// 例如，一个操作系统。当程序丢弃操作系统时，File对象可以使用终结器关闭关联的操作系统文件描述符。文件，但依赖终结器刷新内存中的I/O缓冲区（如
// bufio）将是一个错误。写入程序，因为缓冲区不会在程序退出时刷新。
// 
// 如果*obj的大小为
// 零字节，则不能保证终结器将运行。
// 
// 不保证终结器会为包级变量的初始值设定项中分配的
// 对象运行。这些对象可能是
// 链接器分配的，而不是堆分配的。
// 
// 一旦无法访问对象，终结器就可以运行。
// 为了正确使用终结器，程序必须确保
// 对象在不再需要之前是可访问的。
// 可以访问存储在全局变量中的对象，或者可以通过跟踪
// 全局变量的指针找到的对象。对于其他对象，
// 将对象传递给KeepAlive函数的调用，以标记
// 函数中必须可访问对象的最后一点。
// 
// 例如，如果p指向一个结构，例如os。文件，其中包含一个文件描述符d，p有一个终结器关闭该文件
// 描述符，如果函数中最后一次使用p是调用
// 系统调用。Write（p.d，buf，size），那么一旦
// 程序进入syscall，就可能无法访问p。写终结器可能会在此时运行，
// 关闭p.d，导致系统调用。写入失败，因为它正在写入
// 一个关闭的文件描述符（或者更糟的是，写入一个完全不同的
// 由另一个goroutine打开的文件描述符）。为了避免这个问题，
// 调用runtime。在调用syscall之后保持激活（p）。写
// 
// 单个goroutine按顺序运行程序的所有终结器。
// 如果终结器必须运行很长时间，它应该通过启动一个新的goroutine来完成。
func SetFinalizer(obj any, finalizer any) {
	if debug.sbrk != 0 {
		// 调试。sbrk从不释放内存，因此没有终结器运行
		// （我们没有记录它们的数据结构）。
		return
	}
	e := efaceOf(&obj)
	etyp := e._type
	if etyp == nil {
		throw("runtime.SetFinalizer: first argument is nil")
	}
	if etyp.kind&kindMask != kindPtr {
		throw("runtime.SetFinalizer: first argument is " + etyp.string() + ", not pointer")
	}
	ot := (*ptrtype)(unsafe.Pointer(etyp))
	if ot.elem == nil {
		throw("nil elem type!")
	}

	// 查找包含的对象
	base, _, _ := findObject(uintptr(e.data), 0, 0)

	if base == 0 {
		// 0长度的对象可以。
		if e.data == unsafe.Pointer(&zerobase) {
			return
		}

		// 全局初始值设定项可能是链接器分配的。
		// var Foo=&Object{}
		// func main（）{
		// runtime.SetFinalizer（Foo，nil）
		// }
		// 相关的段是：noptrdata、data、bss、noptrbss。
		// 由于外部链接，我们不能假设它们是以任何顺序甚至是连续的。
		for datap := &firstmoduledata; datap != nil; datap = datap.next {
			if datap.noptrdata <= uintptr(e.data) && uintptr(e.data) < datap.enoptrdata ||
				datap.data <= uintptr(e.data) && uintptr(e.data) < datap.edata ||
				datap.bss <= uintptr(e.data) && uintptr(e.data) < datap.ebss ||
				datap.noptrbss <= uintptr(e.data) && uintptr(e.data) < datap.enoptrbss {
				return
			}
		}
		throw("runtime.SetFinalizer: pointer not in allocated block")
	}

	if uintptr(e.data) != base {
		// 作为一个实现细节，我们允许为对象的内部字节
		// 设置终结器，如果它可能来自tiny alloc（有关详细信息，请参阅mallocgc）。
		if ot.elem == nil || ot.elem.ptrdata != 0 || ot.elem.size >= maxTinySize {
			throw("runtime.SetFinalizer: pointer not at beginning of allocated block")
		}
	}

	f := efaceOf(&finalizer)
	ftyp := f._type
	if ftyp == nil {
		// 切换到系统堆栈并删除终结器
		systemstack(func() {
			removefinalizer(e.data)
		})
		return
	}

	if ftyp.kind&kindMask != kindFunc {
		throw("runtime.SetFinalizer: second argument is " + ftyp.string() + ", not a function")
	}
	ft := (*functype)(unsafe.Pointer(ftyp))
	if ft.dotdotdot() {
		throw("runtime.SetFinalizer: cannot pass " + etyp.string() + " to finalizer " + ftyp.string() + " because dotdotdot")
	}
	if ft.inCount != 1 {
		throw("runtime.SetFinalizer: cannot pass " + etyp.string() + " to finalizer " + ftyp.string())
	}
	fint := ft.in()[0]
	switch {
	case fint == etyp:
		// 确定-相同类型
		goto okarg
	case fint.kind&kindMask == kindPtr:
		if (fint.uncommon() == nil || etyp.uncommon() == nil) && (*ptrtype)(unsafe.Pointer(fint)).elem == ot.elem {
			// 确定-不相同类型，但两个指针
			// 其中一个未命名，且元素类型相同，因此可赋值。
			goto okarg
		}
	case fint.kind&kindMask == kindInterface:
		ityp := (*interfacetype)(unsafe.Pointer(fint))
		if len(ityp.mhdr) == 0 {
			// ok-满足空接口
			goto okarg
		}
		if iface := assertE2I2(ityp, *efaceOf(&obj)); iface.tab != nil {
			goto okarg
		}
	}
	throw("runtime.SetFinalizer: cannot pass " + etyp.string() + " to finalizer " + ftyp.string())
okarg:
	// 计算返回参数所需的大小
	nret := uintptr(0)
	for _, t := range ft.out() {
		nret = alignUp(nret, uintptr(t.align)) + uintptr(t.size)
	}
	nret = alignUp(nret, goarch.PtrSize)

	// 确保我们有一个终结器goroutine 
	createfing()

	systemstack(func() {
		if !addfinalizer(e.data, (*funcval)(f.data), nret, fint, ot) {
			throw("runtime.SetFinalizer: finalizer already set")
		}
	})
}

// 将KeepAlive标记为noinline，这样就可以很容易地将其检测为内在的。
// go:noinline 

// KeepAlive将其参数标记为当前可访问。
// 这确保了在调用KeepAlive的程序中的点之前，对象不会被释放，其终结器也不会运行。
// 
// 一个非常简单的示例，显示了需要KeepAlive的位置：
// 键入文件结构{d int}
// d，err:=syscall。打开（“/file/path”，syscall.ordonly，0）
// 。。。如果出错，就做点什么！=无
// p:=&文件{d}
// 运行时。SetFinalizer（p，func（p*File）{syscall.Close（p.d）}）
// var buf[10]字节
// n，err:=syscall。Read（p.d，buf[：]）
// /确保在Read返回之前p未最终确定。
// 运行时。KeepAlive（p）
// /在此之后不再使用p。
// 
// 如果没有KeepAlive调用，终结器可以在
// syscall的开头运行。读取，在syscall之前关闭文件描述符。Read使
// 成为实际的系统调用。
// 
// 注意：KeepAlive只能用于防止终结器过早运行。尤其是当与不安全设备一起使用时。指针，
// 不安全数据的有效使用规则。指针仍然适用。
func KeepAlive(x any) {
	// 引入编译器无法消除的x用法。
	// 这确保x在进入时处于活动状态。我们需要x活着
	// 在“defer runtime.KeepAlive（x）”的条目上；见第21402期。
	if cgoAlwaysFalse {
		println(x)
	}
}
